﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Configuration;
using System.ServiceModel;
using System.ServiceModel.Description;


namespace MySimpleWCFService
{
    /// <summary>
    /// Unified service initialization
    /// </summary>
    public class ServiceHostEx : ServiceHost
    {
        #region Consts
        const string defaultServiceHttpPort = "8000";
        const string defaultServiceHttpsPort = "8001";
        const string defaultServiceTcpPort = "9000";
        #endregion

        #region Initialization
        public ServiceHostEx()
            : base()
        {

        }
        
        public ServiceHostEx(Type serviceType, Uri[] baseAddresses)
            : base(serviceType, baseAddresses)
        {

        }

        public ServiceHostEx(Type serviceType)
            : base(serviceType)
        {

        }

        protected override void InitializeRuntime()
        {
            base.InitializeRuntime();
        }

        protected override void ApplyConfiguration()
        {
            base.ApplyConfiguration();
        }
        #endregion

        #region Methods 
        private static void AddHttpEndPoint(ServiceHostEx paramServiceHost, Type paramServiceContract, string paramNameSpace)
        {
            //-- basic Http endpoint
            BasicHttpBinding locHttpBinding = new BasicHttpBinding();
            locHttpBinding.Name = "basicHttp";
            locHttpBinding.Namespace = paramNameSpace;

            locHttpBinding.MaxReceivedMessageSize = 2147483647;
            locHttpBinding.MaxBufferSize = 2147483647;
            locHttpBinding.MaxBufferPoolSize = 2147483647;
            locHttpBinding.TransferMode = TransferMode.Buffered;

            locHttpBinding.SendTimeout = new TimeSpan(0, 10, 0);
            locHttpBinding.ReceiveTimeout = new TimeSpan(0, 10, 0);
            locHttpBinding.OpenTimeout = new TimeSpan(0, 10, 0);
            locHttpBinding.CloseTimeout = new TimeSpan(0, 10, 0);

            locHttpBinding.ReaderQuotas.MaxArrayLength = 2147483647;
            locHttpBinding.ReaderQuotas.MaxBytesPerRead = 2147483647;
            locHttpBinding.ReaderQuotas.MaxDepth = 2147483647;
            locHttpBinding.ReaderQuotas.MaxNameTableCharCount = 2147483647;

            locHttpBinding.ReaderQuotas.MaxStringContentLength = 2147483647;

            ServiceEndpoint sepHttp = paramServiceHost.AddServiceEndpoint(paramServiceContract, locHttpBinding, "");
            sepHttp.Name = "basicHttp";
        }

        private static void AddTcpEndPoint(ServiceHostEx paramServiceHost, Type paramServiceContract, string paramNameSpace)
        {
            //-- basic Http endpoint
            NetTcpBinding locNetTcpBinding = new NetTcpBinding();
            locNetTcpBinding.Name = "NetTcp";
            locNetTcpBinding.Namespace = paramNameSpace;

            locNetTcpBinding.MaxReceivedMessageSize = 2147483647;
            locNetTcpBinding.MaxBufferSize = 2147483647;
            locNetTcpBinding.MaxBufferPoolSize = 2147483647;
            locNetTcpBinding.TransferMode = TransferMode.Buffered;

            locNetTcpBinding.SendTimeout = new TimeSpan(0, 10, 0);
            locNetTcpBinding.ReceiveTimeout = new TimeSpan(0, 10, 0);
            locNetTcpBinding.OpenTimeout = new TimeSpan(0, 10, 0);
            locNetTcpBinding.CloseTimeout = new TimeSpan(0, 10, 0);

            locNetTcpBinding.ReaderQuotas.MaxArrayLength = 2147483647;
            locNetTcpBinding.ReaderQuotas.MaxBytesPerRead = 2147483647;
            locNetTcpBinding.ReaderQuotas.MaxDepth = 2147483647;
            locNetTcpBinding.ReaderQuotas.MaxNameTableCharCount = 2147483647;

            locNetTcpBinding.ReaderQuotas.MaxStringContentLength = 2147483647;

            ServiceEndpoint sepHttp = paramServiceHost.AddServiceEndpoint(paramServiceContract, locNetTcpBinding, "");

            string locNameSuffix = paramServiceHost.Description.ConfigurationName.Replace(".", "_");


            sepHttp.Name = "NetTcp" + "_" + locNameSuffix;
        }

        private static void InitializeServiceBehavoirs(ServiceHostEx paramServiceHost)
        {

            //-- setting serviceMetadata
            ServiceMetadataBehavior locServiceMetadataBehavior = null;
            locServiceMetadataBehavior = (ServiceMetadataBehavior)paramServiceHost.Description.Behaviors.Find<ServiceMetadataBehavior>();
            if (locServiceMetadataBehavior == null)
            {
                locServiceMetadataBehavior = new ServiceMetadataBehavior();
                paramServiceHost.Description.Behaviors.Add(locServiceMetadataBehavior);
            };
            locServiceMetadataBehavior.HttpGetEnabled = true;

            //-- setting serviceDebug
            ServiceDebugBehavior locServiceDebugBehavior = null;
            locServiceDebugBehavior = (ServiceDebugBehavior)paramServiceHost.Description.Behaviors.Find<ServiceDebugBehavior>();
            if (locServiceDebugBehavior == null)
            {
                locServiceDebugBehavior = new ServiceDebugBehavior();
                paramServiceHost.Description.Behaviors.Add(locServiceDebugBehavior);
            }
            locServiceDebugBehavior.IncludeExceptionDetailInFaults = true;

            //
            ContractDescription cd = paramServiceHost.Description.Endpoints[0].Contract;

            //-- setting serviceDebug

            foreach (OperationDescription op in cd.Operations)
            {
                DataContractSerializerOperationBehavior locDataContractSerializerBehavior = null;
                locDataContractSerializerBehavior = (DataContractSerializerOperationBehavior)op.Behaviors.Find<DataContractSerializerOperationBehavior>();

                if (locDataContractSerializerBehavior == null)
                {
                    locDataContractSerializerBehavior = new DataContractSerializerOperationBehavior(op);
                    op.Behaviors.Add(locDataContractSerializerBehavior);
                }
                locDataContractSerializerBehavior.MaxItemsInObjectGraph = 2147483647;
                locDataContractSerializerBehavior.IgnoreExtensionDataObject = true;
            }

            //-- setting ServiceThrottlingBehavior 
            ServiceThrottlingBehavior locServiceThrottlingBehavior = null;
            locServiceThrottlingBehavior = (ServiceThrottlingBehavior)paramServiceHost.Description.Behaviors.Find<ServiceThrottlingBehavior>();
            if (locServiceThrottlingBehavior == null)
            {
                locServiceThrottlingBehavior = new ServiceThrottlingBehavior();
                paramServiceHost.Description.Behaviors.Add(locServiceThrottlingBehavior);
            }
            locServiceThrottlingBehavior.MaxConcurrentCalls = 100;      // default 16
            locServiceThrottlingBehavior.MaxConcurrentInstances = 100;  // default 26
            locServiceThrottlingBehavior.MaxConcurrentSessions = 100;   // default 10

        }

        private static Uri[] GetServiceUriArray(
            Type paramServiceType,
            string paramAddressSuffix)
        {
            string addressSuffix;
            string hostName;
            string serviceHttpPort;
            string serviceHttpsPort;
            string serviceNetTcpPort;

            if (ConfigurationManager.AppSettings["WcfHostName"] != null)
                hostName = ConfigurationManager.AppSettings["WcfHostName"];
            else 
                hostName = "localhost";

            if ((paramAddressSuffix != null) && (paramAddressSuffix != String.Empty))
                addressSuffix = "/" + paramAddressSuffix;
            else
                addressSuffix = "";

            if (ConfigurationManager.AppSettings["HTTPPort"] != null)
                serviceHttpPort = ConfigurationManager.AppSettings["HTTPPort"];
            else
                serviceHttpPort = defaultServiceHttpPort;
            if (ConfigurationManager.AppSettings["HttpsPort"] != null)
                serviceHttpsPort = ConfigurationManager.AppSettings["HttpsPort"];
            else
                serviceHttpsPort = defaultServiceHttpsPort;
            if (ConfigurationManager.AppSettings["NetTcpPort"] != null)
                serviceNetTcpPort = ConfigurationManager.AppSettings["NetTcpPort"];
            else
                serviceNetTcpPort = defaultServiceTcpPort;


            Uri locUriHttp = new Uri(
                "http://" +
                hostName + ":" + serviceHttpPort + "/" +
                paramServiceType.Namespace + "/" +
                paramServiceType.Name +
                addressSuffix
            );

            Uri locUriHttps = new Uri(
                "https://" +
                hostName + ":" + serviceHttpsPort + "/" +
                paramServiceType.Namespace + "/" +
                paramServiceType.Name +
                addressSuffix
                //+"/secured"
            );

            Uri locUriTcp = new Uri(
                "net.tcp://" +
                hostName + ":" + serviceNetTcpPort + "/" +//sServiceTcpPort + "/" +
                paramServiceType.Namespace + "/" +
                paramServiceType.Name +
                addressSuffix
            );

            return new Uri[] { 
                    locUriHttp, 
                    locUriHttps, 
                    locUriTcp 
                };
        }
        #endregion

        #region Initializers
        /// <summary>
        /// Initialize object (HTTP only)
        /// </summary>
        public static ServiceHostEx CreateServiceHostWithHttpEndpoints(
            Type serviceType,
            Type serviceContract,
            string paramNameSpace)
        {
            return CreateServiceHostWithHttpEndpoints(
                serviceType,
                serviceContract,
                paramNameSpace,
                "");
        }

        /// <summary>
        /// Initialize object (HTTP only)
        /// </summary>
        public static ServiceHostEx CreateServiceHostWithHttpEndpoints(
            Type paramServiceType,
            Type paramServiceContract,
            string paramNameSpace,
            string paramAddressSuffix)
        {
            ServiceHostEx result = new ServiceHostEx(paramServiceType,
                GetServiceUriArray(paramServiceType, paramAddressSuffix)
                );

            result.CloseTimeout = new TimeSpan(0, 10, 0);
            result.OpenTimeout = new TimeSpan(0, 10, 0);

            AddHttpEndPoint(result, paramServiceContract, paramNameSpace);

            InitializeServiceBehavoirs(result);

            return result;
        }

        /// <summary>
        /// Initialize object (HTTP + NET.TCP)
        /// </summary>
        public static ServiceHostEx CreateServiceHostWithHttpAndTcpEndpoints(
            Type serviceType,
            Type serviceContract,
            string paramNameSpace)
        {
            return CreateServiceHostWithHttpAndTcpEndpoints(
                serviceType,
                serviceContract,
                paramNameSpace,
                "");
        }

        /// <summary>
        /// Initialize object (HTTP + NET.TCP)
        /// </summary>
        public static ServiceHostEx CreateServiceHostWithHttpAndTcpEndpoints(
            Type paramServiceType,
            Type paramServiceContract,
            string paramNameSpace,
            string paramAddressSuffix)
        {

            ServiceHostEx result = new ServiceHostEx(paramServiceType,
                GetServiceUriArray(paramServiceType, paramAddressSuffix)
                );

            result.CloseTimeout = new TimeSpan(0, 10, 0);
            result.OpenTimeout = new TimeSpan(0, 10, 0);

            AddHttpEndPoint(result, paramServiceContract, paramNameSpace);
            AddTcpEndPoint(result, paramServiceContract, paramNameSpace);

            InitializeServiceBehavoirs(result);

            return result;
        }
        #endregion
    }


}
